import {
  type ComponentNode,
  createAbortSignal,
  createElement,
  createKey,
  createList,
  createRoot,
  Fragment,
  suspend,
  Suspense,
  unsuspend,
  update,
} from "../src/client.ts";

// const Theme = createChannel<"light" | "dark">("light");

// function ThemeProvider(
//   this: ComponentNode,
//   props: JSX.PropsWithChildren,
// ) {
//   // Provide (idempotent across re-renders unless you explicitly change value)
//   const theme = Theme.provide(this, "light");
//   // Example: toggle every 2s
//   setInterval(() => {
//     theme.set((prev) => (prev === "light" ? "dark" : "light"));
//   }, 2000);
//   return () => props().children;
// }

// function Badge(this: ComponentNode) {
//   const theme = Theme.use(this);
//   return (
//     <div
//       style={({
//         background: () => theme() === "dark" ? "#222" : "#eee",
//         color: () => theme() === "dark" ? "#eee" : "#222",
//         padding: "4px 8px",
//         borderRadius: "4px",
//       })}
//     >
//       Theme: {theme}
//       <button
//         type="button"
//         onclick={() => {
//           theme.set((prev) => (prev === "light" ? "dark" : "light"));
//         }}
//         style={{ marginLeft: "8px" }}
//       >
//         toggle
//       </button>
//       <button
//         type="button"
//         onclick={() => {
//           theme.update(); // force consumers to re-render without changing value
//         }}
//         style={{ marginLeft: "4px" }}
//       >
//         force update
//       </button>
//     </div>
//   );
// }

// function Nested(this: ComponentNode) {
//   // Locally override provider:
//   Theme.provide(this, "dark");
//   const theme = Theme.use(this);
//   return <div>Nested local theme: {theme}</div>;
// }

function SuspendedChild(this: ComponentNode) {
  let state = "suspended";

  (async () => {
    suspend(this);
    await new Promise((resolve) => setTimeout(resolve, 1000));
    state = "unsuspended";
    update(this);
    unsuspend(this);
  })();

  return (
    <div>
      <h1>I am {() => state}</h1>
    </div>
  );
}

// function CounterSignal(
//   this: ComponentNode,
//   props: JSX.Props<{ count: number }>,
// ) {
//   const countSignal = createSignal(props().count);

//   return (
//     <button
//       type="button"
//       onclick={() => {
//         countSignal(countSignal() + 1);
//       }}
//       disabled={countSignal((c) => c >= 10)}
//       style={{
//         color: "orange",
//       }}
//     >
//       Signal: {countSignal}
//     </button>
//   );
// }

function Drummer(
  this: ComponentNode,
  props: JSX.Props<{ bpm: () => number }>,
) {
  const { bpm } = props();
  const signal = createAbortSignal(this);

  // let interval: ReturnType<typeof setInterval>;
  // const setup = () => {
  //   clearInterval(interval);
  //   interval = setInterval(() => {
  //     console.log('BPM', bpm());
  //   }, bpm());
  // };
  // this.addEventListener('update', setup, { signal });
  // setup();

  document.addEventListener(
    "keydown",
    (e) => {
      if (e.key === "ArrowUp") {
        e.preventDefault();
        e.stopImmediatePropagation();
        this.dispatchEvent(new CustomEvent("bpm:increment", { bubbles: true }));
      }
      if (e.key === "ArrowDown") {
        e.preventDefault();
        e.stopImmediatePropagation();
        this.dispatchEvent(new CustomEvent("bpm:decrement", { bubbles: true }));
      }
    },
    { signal },
  );

  const random = Math.random();

  return () => (
    <div
      className="asd"
      style={{
        color: `hsl(${bpm()},95%,55%)`,
      }}
    >
      {bpm() > 120 ? <s>down</s> : <u>up</u>} [{bpm()}]{" "}
      <strong>{random}</strong> asd
    </div>
  );

  // return (
  //   <div
  //     className='asd'
  //     style={({
  //       color: () => `hsl(${bpm()},95%,55%)`,
  //     })}
  //   >
  //     {() => (bpm() > 120 ? <s>down</s> : <u>up</u>)} [{bpm}]{' '}
  //     {() => <strong>{Math.random()}</strong>} asd
  //   </div>
  // );
}

function CustomInput(
  this: ComponentNode,
  props: JSX.Props<{ defaultValue?: string }>,
) {
  let value = props().defaultValue || "";

  return (
    <>
      <input
        type="text"
        value={() => value}
        oninput={(e) => {
          const input = e.target as HTMLInputElement;
          value = input.value;
          update(this);
        }}
      />
      <br />
      {() => value}
    </>
  );
}

function Counter(this: ComponentNode, props: JSX.Props<{ count?: number }>) {
  let count = props().count || 0;
  return (
    <button
      type="button"
      onclick={() => {
        count++;
        update(this);
      }}
      disabled={() => count >= 10}
    >
      {() => count}
    </button>
  );
}

let createNanoEvents = () => ({
  emit(event, ...args) {
    for (
      let callbacks = this.events[event] || [],
        i = 0,
        length = callbacks.length;
      i < length;
      i++
    ) {
      callbacks[i](...args);
    }
  },
  events: {},
  on(event, cb) {
    (this.events[event] ||= []).push(cb);
    return () => {
      this.events[event] = this.events[event]?.filter((i) => cb !== i);
    };
  },
});

function CounterSubscribable(
  this: ComponentNode,
  props: JSX.Props<{ count: number }>,
) {
  const count = {
    ev: createNanoEvents(),
    _value: props().count,
    get value() {
      return this._value;
    },
    set value(v: number) {
      this._value = v;
      this.ev.emit("up", this._value);
    },
    subscribe(callback: () => void) {
      console.log("SUBSCRIBE", this);
      callback(this.value);
      const off = this.ev.on("up", callback);
      return {
        unsubscribe: () => {
          console.log("UNSUBSCRIBE");
          off();
        },
      };
    },
    map(transform: (v: number) => any) {
      const parent = this;
      return {
        subscribe(callback: (v: any) => void) {
          const wrapped = () => callback(transform(parent.value));
          // call immediately with mapped value
          wrapped();
          const off = parent.ev.on("up", wrapped);
          return {
            unsubscribe: () => {
              off();
            },
          };
        },
        get value() {
          return transform(parent.value);
        },
        // expose emitter for compatibility
        ev: parent.ev,
      };
    },
  };

  return (
    <button
      type="button"
      onclick={() => {
        count.value++;
      }}
      disabled={count.map((v) => v >= 10)}
    >
      {count}
    </button>
  );
}

function Tab1(this: ComponentNode) {
  const signal = createAbortSignal(this);

  signal.addEventListener("abort", () => console.log("aborted"));

  return <div>Tab1</div>;
}

function Tab2(this: ComponentNode) {
  const events: string[] = [];
  this.addEventListener("connect", () => {
    console.log("Connected 1", this.isConnected);
  });
  this.addEventListener("disconnect", () => {
    console.log("Disconnected 1", this.isConnected);
  });
  return (
    <form
      onsubmit={(e) => {
        e.preventDefault();
        const formData = new FormData(e.target);
        const formObject = Object.fromEntries(formData.entries());

        events.push(formObject.event);
        e.target.reset();
        update(this);
      }}
    >
      <input type="text" name="event" />
      <button type="submit">submit</button>
      <ul>{() => events.map((event) => <li>{event}</li>)}</ul>
    </form>
  );
}

function Tab3(this: ComponentNode) {
  return <strong>{Math.random()}</strong>;
}

function Tabber(this: ComponentNode) {
  let tab = "tab1";

  return (
    <div>
      <button
        type="button"
        onclick={() => {
          tab = "tab1";
          update(this);
        }}
      >
        tab1
      </button>
      <button
        type="button"
        onclick={() => {
          tab = "tab2";
          update(this);
        }}
      >
        tab2
      </button>
      <button
        type="button"
        onclick={() => {
          tab = "tab3";
          update(this);
        }}
      >
        tab3 (random key)
      </button>
      <button
        type="button"
        onclick={() => {
          tab = "tab4";
          update(this);
        }}
      >
        tab4 (scoped style)
      </button>
      <div>
        {() => (tab === "tab1"
          ? <Tab1 />
          : tab === "tab2"
          ? <Tab2 />
          : tab === "tab3"
          ? createKey(() => <Tab3 />, Math.random())
          : <Styling />)}
      </div>
    </div>
  );
}

function Sub1(this: ComponentNode) {
  // setInterval(() => {
  //   update(this);
  // }, 1000);

  return () => <Sub2 value={Math.random()} />;
}

function Sub2(props: JSX.Props<{ value: number }>) {
  console.log("render");
  return <h3>Value: {() => props().value}</h3>;
}

function StyledCounterChild(
  this: ComponentNode,
  props: JSX.Props<{ count: number }>,
) {
  let prevCount = props().count;

  this.addEventListener("update", () => {
    console.log("Update");
    // do something fancy
  });

  const span = (
    <span
      style={{
        display: "inline-block",
        fontSize: "24px",
        color: () => props().count < 0 ? "red" : "green",
      }}
      onclick={(event) => event.target}
    >
      {() => (console.log("update??"), props().count)}
    </span>
  );

  span.addEventListener("update", (event) => {
    if (props().count === prevCount) {
      return;
    }

    const frames = props().count >= prevCount
      ? [
        { transform: "translateY(-10px) scale(1.5)" },
        { transform: "translateY(0) scale(1)" },
        { transform: "translateY(0) scale(1)" },
        { transform: "translateY(0) scale(1)" },
      ]
      : [
        { transform: "translateY(10px) scale(0.5)" },
        { transform: "translateY(0) scale(1)" },
        { transform: "translateY(0) scale(1)" },
        { transform: "translateY(0) scale(1)" },
      ];
    prevCount = props().count;

    event.target.animate(frames, {
      duration: 400,
      easing: "ease-out",
      fill: "none",
    });
  });

  return span;
}

function StyledCounter(this: ComponentNode) {
  let count = 0;

  this.addEventListener("update", (e) => {
    console.log("PREVENT1");
    if (count % 2) {
      console.log("PREVENT2");
      e.preventDefault();
      e.stopImmediatePropagation();
      e.stopPropagation();
    }
  });

  return () => (
    <div style={{ overflow: "hidden" }}>
      <button
        onclick={() => {
          count++;
          update(this);
        }}
      >
        +
      </button>
      <button
        onclick={() => {
          count--;
          update(this);
        }}
      >
        -
      </button>
      <StyledCounterChild count={count} />
    </div>
  );
}

function css(
  strings: TemplateStringsArray,
  ...values: any[]
): HTMLStyleElement {
  const parts: any[] = [];

  // If invoked as tagged template: css`...`
  if (Array.isArray(strings)) {
    for (let i = 0; i < strings.length; i++) {
      parts.push(strings[i]);
      if (i < values.length) {
        parts.push(values[i]);
      }
    }
  }

  return createElement("style", null, parts) as any;
}

function getRandomColor() {
  const letters = "0123456789ABCDEF";
  let color = "#";
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

function linkStyles(component: Node, styles: HTMLStyleElement[]) {
  for (const ref of styles) {
    document.head.appendChild(ref);
  }
  component.addEventListener("disconnect", () => {
    for (const ref of styles) {
      ref.remove();
    }
  });
  component.addEventListener("update", () => {
    console.log("Updating styles");
    for (const ref of styles) {
      update(ref);
    }
  });
}

function Styling(this: ComponentNode) {
  linkStyles(this, [css`
    body {
      color: ${getRandomColor};
    }
  `]);

  return (
    <div>
      <button type="button" onclick={() => update(this)}>Recolor</button>
    </div>
  );
}

function ExecEchoReactive(this: HTMLElement, props: JSX.PropsWithChildren) {
  return (
    <pre>
      <ul>
        <li>Direct children: {props().children}</li>
        <li>Random: {Math.random()}</li>
      </ul>
    </pre>
  );
}

/* function child passed directly and updated */
function FnChildParent(this: HTMLElement) {
  let value = "fn";
  return (
    <ExecEchoReactive>
      <button
        type="button"
        onclick={() => {
          value = Math.random().toString();
          update(this);
        }}
      >
        update
      </button>

      {() => value}
    </ExecEchoReactive>
  );
}

function ChildError() {
  throw new Error("error happened");

  return <h1>This is never reached</h1>;
}

// document.addEventListener("update", (e) => {
//   console.log("++++", e.target, e.target.isConnected);
// }, { capture: true });

function ErrorBoundary(
  this: HTMLElement,
  props: JSX.PropsWithChildren<{ fallback: (err: Error) => JSX.Element }>,
) {
  let error: Error | null = null;

  this.addEventListener(
    "error",
    (e: Event) => {
      e.preventDefault();
      e.stopPropagation();

      const ce = e as ErrorEvent;
      error = ce?.error || new Error("Unknown error");

      update(this);
    },
  );

  return () => {
    if (error) {
      return props().fallback(error);
    }

    return props().children;
  };
}

function AsyncChild(this: HTMLElement) {
  suspend(this);
  setTimeout(() => {
    unsuspend(this);
  }, 1500);
  return () => <span>Child {Math.random()}</span>;
}

function App(this: ComponentNode, props: JSX.Props<{ name: string }>) {
  let bpm = 120;

  const signal = createAbortSignal(this);
  this.parentElement?.addEventListener(
    "bpm:increment",
    () => {
      bpm++;
      update(this);
    },
    { signal },
  );

  this.parentElement?.addEventListener(
    "bpm:decrement",
    () => {
      bpm--;
      update(this);
    },
    { signal },
  );

  const el = <strong>this is gray</strong>;
  el.style.color = "gray";

  const customElement = document.createElement("strong");
  customElement.innerHTML = "Hello from custom createElement";

  return (
    <div>
      {customElement}
      <h1>
        Hey {() => props().name} {() => bpm}
      </h1>
      {el}
      <br />
      <Counter count={5} />
      <Counter count={2} />
      {/*<CounterSignal count={1} />*/}
      <CounterSubscribable count={0} />
      <Drummer bpm={() => bpm} />
      <Drummer bpm={() => 120 - bpm} />
      <CustomInput defaultValue="Hey" />
      <Tabber />
      <hr />
      Dictionary:
      <div>
        <Dictionary />
      </div>
      <hr />
      <div>
        Suspense:
        <div>
          <Suspense fallback={() => <strong>Loading...</strong>}>
            <SuspendedChild />
            asd
          </Suspense>
        </div>
        <div>
          <Suspense fallback={() => <em>Global fallback...</em>}>
            <AsyncChild />
          </Suspense>
        </div>
      </div>
      <hr />
      {
        /*<ThemeProvider>
        <h1>Channel Demo</h1>
        <Badge />
        <Nested />
      </ThemeProvider>*/
      }
      <hr />
      <Sub1 />
      <hr />
      <StyledCounter />
      <hr />
      <FnChildParent />
      <hr />
      Before
      <ErrorBoundary
        fallback={(error) => (
          <strong style={{ color: "orangered" }}>
            Child error: {String(error)}
          </strong>
        )}
      >
        <ChildError />
      </ErrorBoundary>
      {/*<ErrorBoundary2><Boom /></ErrorBoundary2>*/}
      After
      <hr />
      Lifecycle Demo:
      <EventLifecycleDemo />
      <hr />
      Event Propagation Demo:
      <EventPropagationParent id="1">
        <EventPropagationParent id="2" onevent={(e) => e.stopPropagation()}>
          <EventPropagationParent id="3">
            <EventPropagationDemoChild />
          </EventPropagationParent>
        </EventPropagationParent>
      </EventPropagationParent>
      <hr />
      Identity swap:
      <div>
        <IdentitySwap />
      </div>
      <div>
        <VariableArrayRoot />
      </div>
      <div>
        <DynamicParent />
      </div>
    </div>
  );
}

function EchoReactive(this: HTMLElement, props: JSX.PropsWithChildren) {
  return <div className="echo-reactive">{() => props().children}</div>;
}

function DynamicParent(this: HTMLElement) {
  let itemCount = 1;
  let itemsA = ["a", "b"];
  let itemsB = ["b", "c", "d"];
  // (this as any).__setItems = (next: string[]) => {
  //   items = next.slice();
  //   update(this);
  // };
  // const key = createKeys(this);

  // return () => (
  //   <div>
  //     <button
  //       type="button"
  //       onclick={() => {
  //         itemCount++;
  //         update(this);
  //       }}
  //     >
  //       Dynamic parent + ({itemCount})
  //     </button>
  //     <EchoReactive>
  //       1[
  //       {(itemCount % 2 ? itemsA : itemsB).map((v) => (
  //         key(() => <span className="dyn">{v}</span>, v)
  //       ))}
  //       ]2
  //     </EchoReactive>
  //   </div>
  // );
  return null;
}

function Item(this: HTMLElement, props: JSX.Props<{ id: number }>) {
  console.log("OPENED", props().id);
  return <span className="item-span">#{() => props().id}</span>;
}

function VariableArrayRoot(this: HTMLElement) {
  let itemCount = 1;
  return () => {
    const items = Array.from({ length: itemCount }, (_, i) => <Item id={i} />);
    const itemsKeyed = createList((key) =>
      Array.from({ length: itemCount }, (_, i) => key(() => <Item id={i} />, i))
    );
    const items2 = Array.from(
      { length: itemCount },
      (_, i) => <span className="item-span">#{() => i}</span>,
    );
    const items2Keyed = createList((key) =>
      Array.from(
        { length: itemCount },
        (_, i) => key(() => <span className="item-span">#{() => i}</span>, i),
      )
    );
    return (
      <div className="var-array">
        <button
          className="inc-btn"
          onclick={() => {
            itemCount++;
            update(this);
          }}
        >
          inc
        </button>
        <button
          className="dec-btn"
          onclick={() => {
            itemCount = Math.max(0, itemCount - 1);
            update(this);
          }}
        >
          dec
        </button>
        <div className="list">items: {items}</div>
        <div className="list">itemsKeyed: {itemsKeyed}</div>
        <div className="list">items2: {items2}</div>
        <div className="list">items2Keyed: {items2Keyed}</div>
        <span className="count">{itemCount}</span>
      </div>
    );
  };
}

function IdentitySwap(this: HTMLElement) {
  let mode: "A" | "B" = "A";
  return () => (
    <div className="identity-swap">
      <button
        className="swap"
        onclick={() => {
          mode = mode === "A" ? "B" : "A";
          update(this);
        }}
      >
        swap
      </button>
      {mode === "A" ? <AChild label="aaa" /> : <BChild label="bbb" />}
      <span className="mode">{mode}</span>
    </div>
  );
}

function AChild(this: HTMLElement, props: JSX.Props<{ label: string }>) {
  let renderCount = 0;
  return () => (
    <div className="a-child">
      A:{props().label} r:{++renderCount}
    </div>
  );
}

function BChild(this: HTMLElement, props: JSX.Props<{ label: string }>) {
  let renderCount = 0;
  return () => (
    <div className="b-child">
      B:{props().label} r:{++renderCount}
    </div>
  );
}

function EventLifecycleDemo(this: HTMLElement) {
  let i = 0;
  console.log("RENDER COMPONENT");
  this.addEventListener("connect", () => {
    console.log("CONNECT", i);
  });
  this.addEventListener("disconnect", () => {
    console.log("DISCONNECT", i);
  });
  this.addEventListener("update", () => {
    console.log("UPDATE", i);
  });
  this.addEventListener("update", (e) => {
    console.log("UPDATE CAPTURE", i, e.target, e.currentTarget);
  }, { capture: true });
  return () => (console.log("RENDER REACTIVE", i),
    (
      <button
        onclick={() => {
          i++;
          update(this);
        }}
      >
        {i}
      </button>
    ));
}

function Boom() {
  throw new Error("boom");
  return <span>OK</span>;
}
function ErrorBoundary2(this: HTMLElement, props: JSX.PropsWithChildren) {
  this.addEventListener("error", (e) => {
    e.preventDefault();
    console.log("Caught:", (e as ErrorEvent).error);
  });
  return () => props().children;
}

// Example: createList for keyed lists
function KeyedListDemo(this: HTMLDivElement) {
  let items = [
    { id: 1, text: "Apple" },
    { id: 2, text: "Banana" },
    { id: 3, text: "Cherry" },
  ];

  return (
    <div>
      <h3>createList Demo</h3>
      <ul>
        {() =>
          createList((key) =>
            items.map((item) => key(() => <li>{item.text}</li>, item.id))
          )}
      </ul>
      <button
        type="button"
        onclick={() => {
          items = items.slice().reverse();
          update(this);
        }}
      >
        Reverse
      </button>
    </div>
  );
}

// Example: createKey for single keyed components
function KeyedComponentDemo(this: HTMLDivElement) {
  let currentKey = "a";
  let count = 0;

  return (
    <div>
      <h3>createKey Demo</h3>
      <p>Current key: {() => currentKey}</p>
      {() =>
        createKey(
          () => <Counter />,
          currentKey,
        )}
      <button
        type="button"
        onclick={() => {
          currentKey = currentKey === "a" ? "b" : "a";
          count++;
          update(this);
        }}
      >
        Toggle Key (remounts component)
      </button>
    </div>
  );
}

// createRoot(document.body).render(<Dictionary />);
createRoot(document.body).render(<App name="World" />);

// /**
//  * Dispatch a fresh "suspend" event from a descendant node.
//  * Returns true if not prevented.
//  */
// export function suspend(target: Node): boolean {
//   return target.dispatchEvent(
//     new Event("suspend", {
//       bubbles: true,
//       composed: true,
//       cancelable: true,
//     }),
//   );
// }

// /**
//  * Dispatch a fresh "unsuspend" event from a descendant node.
//  * Returns true if not prevented.
//  */
// export function unsuspend(target: Node): boolean {
//   return target.dispatchEvent(
//     new Event("unsuspend", {
//       bubbles: true,
//       composed: true,
//       cancelable: true,
//     }),
//   );
// }

// /**
//  * Suspense component
//  * Shows fallback while one or more descendants are suspended.
//  * Child components that perform async work should call suspend(node) before starting
//  * and unsuspend(node) when resolved. Multiple overlapping suspensions are reference-counted.
//  *
//  * This implementation relies on the updated component build queue:
//  * - Suspense host builds first, installs listeners.
//  * - Descendant component builds that trigger suspend will bubble upward correctly.
//  */
// document.body.addEventListener("error", (e) => {
//   console.warn("Global error:", e);
// });
// export function Suspense(
//   this: HTMLElement,
//   props: JSX.PropsWithChildren<{ fallback: JSX.Element }>,
// ) {
//   // Track number of active suspensions.
//   let pending = 0;
//   // Start by assuming children should render; suspend events may arrive during child build.
//   let showChildren = true;

//   const onSuspend = (e: Event) => {
//     console.log("onSuspend");
//     e.preventDefault();
//     e.stopPropagation();
//     if (pending === 0 && showChildren) {
//       showChildren = false;
//       setTimeout(() => {
//         update(this);
//       }, 100);
//     }
//     pending++;
//   };

//   const onUnsuspend = (e: Event) => {
//     console.log("onUnsuspend");
//     e.preventDefault();
//     e.stopPropagation();
//     if (pending > 0) pending--;
//     if (pending === 0 && !showChildren) {
//       showChildren = true;
//       setTimeout(() => {
//         update(this);
//       }, 100);
//     }
//   };

//   // this.addEventListener("update", (e) => {
//   //   console.log("update");
//   //   e.preventDefault();
//   //   // QUESTION? Should we do this?
//   //   // for (const child of props().children) {
//   //   //   if (child instanceof HTMLElement) {
//   //   //     if (child.isConnected) {
//   //   //       update(child);
//   //   //     }
//   //   //   }
//   //   // }
//   // });
//   this.addEventListener("suspend", onSuspend);
//   this.addEventListener("unsuspend", onUnsuspend);
//   this.addEventListener(
//     "disconnect",
//     () => {
//       console.log("disconnect");
//       this.removeEventListener("suspend", onSuspend);
//       this.removeEventListener("unsuspend", onUnsuspend);
//     },
//     { once: true },
//   );

//   const template = (
//     <suspense style={({ display: () => (showChildren ? "contents" : "none") })}>
//       {() =>
//         props().children}
//     </suspense>
//   );

//   return [
//     template,
//     () => showChildren ? null : props().fallback,
//   ];
// }

function EventPropagationParent(
  this: HTMLElement,
  props: JSX.PropsWithChildren<{ id: string; onevent?: (e: Event) => void }>,
) {
  this.addEventListener("click", (e) => {
    props().onevent?.(e);
    console.log("DIV CLICK PARENT", props().id);
  });

  this.addEventListener("poop", (e) => {
    console.log("POOP PARENT", props().id);
  });

  return () => props().children;
}

function EventPropagationDemoChild(this: HTMLElement) {
  this.addEventListener("click", (e) => {
    console.log("DIV CLICK CHILD");
  });

  return (
    <button
      type="button"
      onclick={(e) => {
        console.log("BUTTON CLICK");
      }}
    >
      Click me
    </button>
  );
}

let i = 0;
function Definition(this: ComponentNode, props: JSX.Props<{ word: string }>) {
  const a = i++;
  console.log("loaded", props().word);
  // API courtesy https://dictionaryapi.dev
  let data: any;
  let currentWord: string;

  suspend(this);

  const getData = async () => {
    const word = props().word;

    if (currentWord === word) {
      return;
    }
    currentWord = word;
    data = null;

    const res = await fetch(
      `https://api.dictionaryapi.dev/api/v2/entries/en/${word}`,
    );
    data = await res.json();
    await new Promise((resolve) => setTimeout(resolve, 200));

    update(this);
    unsuspend(this);
  };

  getData();
  // this.addEventListener("update", getData);

  return () => {
    if (!Array.isArray(data)) {
      return <div>No definition found for {props().word}</div>;
    }

    const { phonetic, meanings } = data[0];
    const { partOfSpeech, definitions } = meanings[0];
    const { definition } = definitions[0];

    return (
      <div>
        {a} {i}
        <p>
          {currentWord} <code>{phonetic}</code>
        </p>
        <p>
          <b>{partOfSpeech}.</b> {definition}
        </p>
      </div>
    );
  };
}

function Dictionary(this: HTMLElement) {
  let word = "";
  const onsubmit = (ev: SubmitEvent) => {
    ev.preventDefault();
    const formData = new FormData(ev.target);
    const word1 = formData.get("word") as string;
    console.log("submit", word1);
    if (word1.trim()) {
      word = word1;
      update(this);
    }
  };

  return (
    <div>
      <form
        action=""
        method="get"
        onsubmit={onsubmit}
        style="margin-bottom: 15px"
      >
        <div style="margin-bottom: 15px">
          <label for="name">Define:</label>{" "}
          <input type="text" name="word" id="word" required />
        </div>
        <div>
          <input type="submit" value="Search" />
        </div>
      </form>
      <Suspense fallback={() => <strong>Loading..</strong>}>
        <div>
          {() => (console.log("yep", word),
            word && createKey(() => <Definition word={word} />, word))}
        </div>
      </Suspense>
    </div>
  );
}
